package model

import (
	"encoding/json"
	"fmt"

	"github.com/iancoleman/orderedmap"

	"github.com/invopop/jsonschema"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/bsontype"
)

type (
	Labels map[string]string
)

const (
	labelDelimiter             = "="
	DropAppServiceLabel        = "caas.service.type"
	DropAppServiceLoadBalancer = "LoadBalancer"
)

func (Labels) JSONSchema() *jsonschema.Schema {
	properties := orderedmap.New()
	properties.Set(DropAppServiceLabel, jsonschema.Schema{Type: "string",
		Enum: []interface{}{DropAppServiceLoadBalancer}})

	return &jsonschema.Schema{
		AnyOf: []*jsonschema.Schema{
			{
				Type:       "object",
				Properties: properties,
				PatternProperties: map[string]*jsonschema.Schema{
					".+": {OneOf: []*jsonschema.Schema{
						{Type: "string"},
						{Type: "boolean"},
						{Type: "number"},
					}},
				},
			},
			{
				Type:        "array",
				Items:       &jsonschema.Schema{Type: "string"},
				UniqueItems: true,
			},
		},
	}
}

func (d *Labels) UnmarshalJSON(data []byte) error {
	listOrDict := ListOrDict{}

	if err := json.Unmarshal(data, &listOrDict); err != nil {
		return err
	}

	if listOrDict.Dict != nil {
		*d = ConvertDictToDict(listOrDict.Dict)
		return nil

	}

	*d = ConvertListToDict(listOrDict.List, labelDelimiter)
	return nil
}

func (d *Labels) MarshalJSON() ([]byte, error) {
	var environmentMap map[string]string = *d
	return json.Marshal(environmentMap)
}

func (d *Labels) MarshalBSONValue() (bsontype.Type, []byte, error) {
	var environmentMap map[string]string = *d
	return bson.MarshalValue(environmentMap)
}

func (d *Labels) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
	value := bson.RawValue{Type: t, Value: data}
	listOrDict := ListOrDict{}

	if err := value.Unmarshal(&listOrDict); err != nil {
		return fmt.Errorf("failed to convert labels: %w", err)
	}

	if listOrDict.Dict != nil {
		*d = ConvertDictToDict(listOrDict.Dict)
		return nil
	}

	*d = ConvertListToDict(listOrDict.List, labelDelimiter)
	return nil
}
